package com.longcai.config.shardingjdbc;

import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration;
import io.shardingjdbc.core.jdbc.core.datasource.ShardingDataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

@Configuration
@MapperScan(basePackages = "com.longcai.mapper", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class DataSourceConfig {

    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.initialSize}")
    private int initialSize;
    @Value("${spring.datasource.minIdle}")
    private int minIdle;
    @Value("${spring.datasource.maxActive}")
    private int maxActive;
    @Value("${spring.datasource.maxWait}")
    private int maxWait;
    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
    private long timeBetweenEvictionRunsMillis;
    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private long minEvictableIdleTimeMillis;
    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;
    @Value("${spring.datasource.testWhileIdle}")
    private Boolean testWhileIdle;
    @Value("${spring.datasource.testOnBorrow}")
    private Boolean testOnBorrow;
    @Value("${spring.datasource.testOnReturn}")
    private Boolean testOnReturn;
    @Value("${spring.datasource.poolPreparedStatements}")
    private Boolean poolPreparedStatements;
    @Value("${spring.datasource.connectionProperties}")
    private String connectionProperties;

    /**
     * 设置默认的数据源
     *
     * @return
     */
    @Bean(name = "shardingDataSource")
    DataSource getShardingDataSource() throws SQLException {
        // 具体分表策略，按什么规则来分
        ShardingRuleConfiguration shardingRuleConfig;
        shardingRuleConfig = new ShardingRuleConfiguration();

        shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration_1());
        shardingRuleConfig.getBindingTableGroups().add("clyj_goods_order_details");
        shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration_2());
        shardingRuleConfig.getBindingTableGroups().add("clyj_goods_order_info");
        // 设置数据表策略，传入的是
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration
                ("user_id", TableShardingAlgorithm.class.getName()));

        return new ShardingDataSource(shardingRuleConfig.build(createDataSourceMap()));
    }

    /**
     * 订单详情表具体的分表的策略
     *
     * @return
     */
    @Bean
    TableRuleConfiguration getUserTableRuleConfiguration_1() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
        // 设置分表映射，将user_info_0和user_info_1两个实际的表映射到user_info逻辑表
        orderTableRuleConfig.setLogicTable("clyj_goods_order_details");
        // 设置数据节点，格式为dbxx.tablexx。这里的名称要和map的别名一致
        orderTableRuleConfig.setActualDataNodes("order_db.clyj_goods_order_details_${0..9}");
        // 设置纵列名称
        orderTableRuleConfig.setKeyGeneratorColumnName("id");
        return orderTableRuleConfig;
    }

    /**
     * 订单表具体的分表的策略
     *
     * @return
     */
    @Bean
    TableRuleConfiguration getUserTableRuleConfiguration_2() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
        // 设置分表映射，将user_info_0和user_info_1两个实际的表映射到user_info逻辑表
        orderTableRuleConfig.setLogicTable("clyj_goods_order_info");
        // 设置数据节点，格式为dbxx.tablexx。这里的名称要和map的别名一致
        orderTableRuleConfig.setActualDataNodes("order_db.clyj_goods_order_info_${0..9}");
        // 设置纵列名称
        orderTableRuleConfig.setKeyGeneratorColumnName("id");
        return orderTableRuleConfig;
    }

    /**
     * 需要手动配置事务管理器
     *
     * @param shardingDataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {
        return new DataSourceTransactionManager(shardingDataSource);
    }

    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory(DataSource shardingDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(shardingDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return bean.getObject();
    }

    @Bean
    @Primary
    public SqlSessionTemplate testSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * 分库映射
     *
     * @return
     */
    private Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>(1);
        result.put("order_db", createDataSource());
        return result;
    }

    /**
     * 数据源
     *
     * @return
     */
    private DataSource createDataSource() {
        BasicDataSource result = new BasicDataSource();
        result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
        result.setUrl(String.format(url));
        result.setUsername(username);
        result.setPassword(password);
        result.setInitialSize(initialSize);
        result.setMaxIdle(minIdle);
        result.setMaxActive(maxActive);
        result.setMaxWait(maxWait);
        result.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        result.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        result.setValidationQuery(validationQuery);
        result.setTestWhileIdle(testWhileIdle);
        result.setTestOnBorrow(testOnBorrow);
        result.setTestOnReturn(testOnReturn);
        result.setPoolPreparedStatements(poolPreparedStatements);
        result.setConnectionProperties(connectionProperties);

        return result;
    }
}
